package video

import (
	"errors"
	"gitee.com/mootok/mootok/src/constant/strings"
	"gitee.com/mootok/mootok/src/external_service/censor"
	"gitee.com/mootok/mootok/src/models"
	"gitee.com/mootok/mootok/src/services/feed"
	video2 "gitee.com/mootok/mootok/src/services/video"
	"gitee.com/mootok/mootok/src/storage/database"
	"gitee.com/mootok/mootok/src/storage/es"
	"gitee.com/mootok/mootok/src/storage/file"
	"gitee.com/mootok/mootok/src/utils/logging"
	"gitee.com/mootok/mootok/src/web/api"
	"gitee.com/mootok/mootok/src/web/common/response"
	"github.com/gin-gonic/gin"
	"github.com/sirupsen/logrus"
	"gorm.io/gorm"
	"net/http"
)

const (
	letterBytes  = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
	uploadMaxQPS = 5
)

type ApiVideoImpl struct {
}

var FeedService feed.Service

func (*ApiVideoImpl) PostVideo(c *gin.Context) {
	var req api.PostVideoReq
	var err error

	ctxUserId, _ := c.Get(strings.UserLoginContext)
	userId := ctxUserId.(int)

	err = c.BindJSON(&req)
	if err != nil {
		response.FailWithMsg(c, http.StatusBadRequest, strings.ParamsErrorCode, strings.ParamsError)
		return
	}

	logger := logging.LogService("Video.Post").WithFields(logrus.Fields{
		"user_id":        userId,
		"cover_filename": req.CoverFilename,
		"video_filename": req.VideoFilename,
	})

	videoUploadTask := &models.UploadTask{}

	if err = database.Client.First(videoUploadTask, &models.UploadTask{
		UserId:   userId,
		FileName: req.VideoFilename,
	}).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			logger.Error("video file not found.")
			response.FailWithMsg(c, http.StatusBadRequest, strings.PostFileNameInvalidErrorCode, strings.PostFileNameInvalidError)
			return
		} else {
			logger.Errorf("query video file failed: %v", err)
			response.FailWithMsg(c, http.StatusBadRequest, strings.DataBaseSelectErrorCode, strings.InternalServiceError)
			return
		}
	}
	if err = database.Client.Delete(videoUploadTask).Error; err != nil {
		logger.Warningf("failed to delete posted video task: %v", err)
	}

	exists, err := file.Instance.IsExists(videoUploadTask.FileName)
	if err != nil || !exists {
		logger.Warningf("video file not uploaded: %v", err)
		response.FailWithMsg(c, http.StatusBadRequest, strings.PostFileNameInvalidErrorCode, strings.PostFileNameInvalidError)
		return
	}


	coverUploadTask := &models.UploadTask{}
	if err = database.Client.First(coverUploadTask, &models.UploadTask{
		UserId:   userId,
		FileName: req.CoverFilename,
	}).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			logger.Error("cover file not found.")
			response.FailWithMsg(c, http.StatusBadRequest, strings.PostFileNameInvalidErrorCode, strings.PostFileNameInvalidError)
			return
		} else {
			logger.Errorf("query cover file failed: %v", err)
			response.FailWithMsg(c, http.StatusBadRequest, strings.DataBaseSelectErrorCode, strings.InternalServiceError)
			return
		}
	}
	if err = database.Client.Delete(coverUploadTask).Error; err != nil {
		logger.Warningf("failed to delete posted video task: %v", err)
	}

	exists, err = file.Instance.IsExists(coverUploadTask.FileName)
	if err != nil || !exists {
		logger.Warningf("cover file not uploaded: %v", err)
		response.FailWithMsg(c, http.StatusBadRequest, strings.PostFileNameInvalidErrorCode, strings.PostFileNameInvalidError)
		return
	}


	exists, err = file.Instance.IsExists(coverUploadTask.FileName)
	if err != nil || !exists {
		logger.Warningf("video file not uploaded: %v", err)
		response.FailWithMsg(c, http.StatusBadRequest, strings.PostFileNameInvalidErrorCode, strings.PostFileNameInvalidError)
		return
	}

	jobId, err := censor.Instance.CreateAuditVideoTask(req.VideoFilename)
	if err != nil {
		logger.Warningf("create censor task failed: %v", err)
		response.FailWithMsg(c, http.StatusBadRequest, strings.CensorServerFailedError, strings.ParamsError)
		return
	}
	video := models.Video{
		UserId:      userId,
		Description: req.Description,
		FileName:    req.VideoFilename,
		CoverName:   req.CoverFilename,
		Status: video2.StatusAuditing,
		JobId: jobId,
	}
	if err = database.Client.Create(&video).Error; err != nil {
		logger.Errorf("failed save post video to db: %v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseInsertErrorCode, strings.InternalServiceError)
		return
	}
	videoCategories := make([]models.VideoCategory, len(req.Category))
	for i, id := range req.Category {
		videoCategories[i].CategoryId = id
		videoCategories[i].VideoId = video.ID
	}
	if err = database.Client.Create(&videoCategories).Error; err != nil {
		logger.Warningf("failed save categories info to db: %v", err)
		return
	}

	keywords := extractKeywords(video.Description)

	logger.Infof("parsed keywords: %v", keywords)

	err = es.VideoStore.Create(&models.VideoEsDocument{
		ID:          video.ID,
		UserId:      userId,
		Description: video.Description,
		Keywords:    keywords,
		CreatedAt:   video.CreatedAt,
		UpdatedAt:   video.UpdatedAt,
	})

	if err != nil {
		logger.Warn("es index failed.")
	}

	response.SuccessWithDetail(c, strings.SuccessCode, strings.Success, &api.PostVideoResp{
		PostId: &video.ID,
	})
	logger.Info("video posted.")
}
